package aole.mindfx;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import aole.mindfx.model.AppStructure;
import aole.mindfx.model.MindFXAttribute;
import aole.mindfx.model.MindFXCall;
import aole.mindfx.model.MindFXCallAttribute;
import aole.mindfx.model.MindFXCallParam;
import aole.mindfx.model.MindFXCallThis;
import aole.mindfx.model.MindFXClass;
import aole.mindfx.model.MindFXMethod;
import aole.mindfx.model.MindFXParam;
import aole.mindfx.profile.Profiler;

public class EditMethodPane extends GridPane {

	private Label					label;
	private MindFXMethod			fxmethod;
	private ComboBox< String >		cbTypes;
	private ComboBox< String >		cbScope;
	private ComboBox< Integer >		cbPointers;
	private ListView< MindFXParam >	paramList;
	private ComboBox< String >		cbParamTypes;
	private TextField				txtParamName;
	private ListView< MindFXCall >	callList;
	private ComboBox< String >		cbCallAttribs;
	private ComboBox< String >		cbCallMethods;
	private Profiler				profiler;
	private TextField				txtPurpose;
	private FXNodeListener			listener;
	private ComboBox< Integer >		cbParamPointers;
	private AppStructure			structure;

	class TypeEventHandler implements EventHandler< ActionEvent > {
		@Override
		public void handle( ActionEvent event ) {
			fxmethod.setType( cbTypes.getValue() );
			label.setText( fxmethod.getFullCaption() );
		}
	}

	class PointerEventHandler implements EventHandler< ActionEvent > {
		@Override
		public void handle( ActionEvent event ) {
			fxmethod.setPointers( cbPointers.getValue() );
			label.setText( fxmethod.getFullCaption() );
		}
	}

	class CallAttribsEventHandler implements EventHandler< ActionEvent > {
		@Override
		public void handle( ActionEvent event ) {
			try {
				cbCallMethods.setItems( getMethods( cbCallAttribs.getValue() ) );
			} catch( Exception e ) {}
		}
	}

	class ScopeEventHandler implements EventHandler< ActionEvent > {
		@Override
		public void handle( ActionEvent event ) {
			fxmethod.setScope( cbScope.getValue() );
		}
	}

	class PurposeEventHandler implements EventHandler< KeyEvent > {
		@Override
		public void handle( KeyEvent event ) {
			fxmethod.setPurpose( txtPurpose.getText() );
		}
	}

	class AddParamEventHandler implements EventHandler< ActionEvent > {
		@Override
		public void handle( ActionEvent event ) {
			MindFXParam param = new MindFXParam( txtParamName.getText(),
					fxmethod );
			param.setType( cbParamTypes.getValue() );
			param.setPointers( cbParamPointers.getValue() );
			fxmethod.addFXParam( param );
			listener.actionPerformed( fxmethod );
		}
	}

	class DeleteParamEventHandler implements EventHandler< ActionEvent > {
		@Override
		public void handle( ActionEvent event ) {
			fxmethod.deleteFXParam( paramList.getSelectionModel()
					.getSelectedItem() );
			listener.actionPerformed( fxmethod );
		}
	}

	class AddCallEventHandler implements EventHandler< ActionEvent > {

		@Override
		public void handle( ActionEvent event ) {
			String a = cbCallAttribs.getValue();
			String m = cbCallMethods.getValue();
			if( a == null || m == null ) return;

			// create call
			MindFXCall call;
			if( a.equals( "this" ) ) {
				call = new MindFXCallThis( "this", fxmethod );
			} else {
				MindFXAttribute att = fxmethod.getFXClass().getAttribute( a );
				if( att != null ) {
					call = new MindFXCallAttribute( "attribute", fxmethod,
							structure );
					call.setXMLAttribute( "attribute", a );
				} else {
					call = new MindFXCallParam( "parameter", fxmethod );
					call.setXMLAttribute( "parameter", a );
				}
			}
			call.setXMLAttribute( "method", m );
			// is call from same class as this method's
			MindFXClass cc;
			if( !a.equals( "this" ) ) {
				MindFXAttribute att = fxmethod.getFXClass().getAttribute( a );
				if( att != null ) {
					cc = profiler.getFXClass( att.getType() );
				} else {
					cc = profiler.getFXClass( fxmethod.getFXParam( a )
							.getType() );
				}
			} else {// from same class
				cc = fxmethod.getFXClass();
			}
			// add call reference to the called method
			MindFXMethod cm = cc.getMethod( m );
			cm.addFXCallReference( cc.getName(), m );

			fxmethod.addFXCall( call );
		}
	}

	class DeleteCallEventHandler implements EventHandler< ActionEvent > {
		@Override
		public void handle( ActionEvent event ) {
			fxmethod.deleteFXCall( callList.getSelectionModel()
					.getSelectedItem() );
		}
	}

	class SimulationEventHandler implements EventHandler< ActionEvent > {
		@Override
		public void handle( ActionEvent event ) {
			listener.onSimulation( fxmethod );
		}
	}

	class DeleteEventHandler implements EventHandler< ActionEvent > {
		@Override
		public void handle( ActionEvent event ) {
			fxmethod.getFXClass().deleteFXMethod( fxmethod );
		}
	}

	public EditMethodPane( FXNodeListener l, Profiler prof, AppStructure s ) {
		super();
		listener = l;
		setPadding( new Insets( 12, 12, 12, 12 ) );
		setHgap( 5 );
		setVgap( 5 );
		profiler = prof;
		structure = s;

		int row = 1;
		// attribute label
		label = new Label();
		label.setStyle( MindFXMethod.getStyle() );
		label.setFont( MindFXMethod.getFont() );
		add( label, 1, row, 2, 1 );
		row++;

		// delete button
		Button b = new Button( "Delete" );
		b.setOnAction( new DeleteEventHandler() );
		add( b, 2, row, 1, 1 );
		row++;

		// attribute type
		Label type = new Label( "Type" );
		add( type, 1, row, 1, 1 );
		cbTypes = new ComboBox< String >();
		cbTypes.setItems( prof.getMethodTypes() );
		cbTypes.setOnAction( new TypeEventHandler() );
		add( cbTypes, 2, row, 1, 1 );
		row++;

		// attribute scope
		Label scope = new Label( "Scope" );
		add( scope, 1, row, 1, 1 );
		cbScope = new ComboBox< String >();
		cbScope.setItems( prof.getMethodScopes() );
		cbScope.setOnAction( new ScopeEventHandler() );
		add( cbScope, 2, row, 1, 1 );
		row++;

		// pointers
		add( new Label( "Pointers" ), 1, row, 1, 1 );
		cbPointers = new ComboBox< Integer >();
		cbPointers.setItems( FXCollections.observableArrayList( 0, 1, 2 ) );
		cbPointers.setOnAction( new PointerEventHandler() );
		add( cbPointers, 2, row, 1, 1 );
		row++;

		// Parameters
		add( new Label( "Parameters" ), 1, row, 2, 1 );
		row++;
		paramList = new ListView< MindFXParam >();
		add( paramList, 1, row, 2, 1 );
		row++;
		cbParamTypes = new ComboBox< String >();
		cbParamTypes.setItems( prof.getAttributeTypes() );
		cbParamTypes.setValue( "int" );
		add( cbParamTypes, 1, row, 1, 1 );
		txtParamName = new TextField();
		add( txtParamName, 2, row, 1, 1 );
		row++;
		add( new Label( "Pointers" ), 1, row, 1, 1 );
		cbParamPointers = new ComboBox< Integer >();
		cbParamPointers.setItems( FXCollections.observableArrayList( 0, 1, 2 ) );
		cbParamPointers.setValue( 0 );
		add( cbParamPointers, 2, row, 1, 1 );
		row++;
		b = new Button( "Add Parameter" );
		b.setOnAction( new AddParamEventHandler() );
		add( b, 1, row, 1, 1 );
		b = new Button( "Delete Selected Param" );
		b.setOnAction( new DeleteParamEventHandler() );
		add( b, 2, row, 1, 1 );
		row++;

		// purpose
		add( new Label( "Purpose" ), 1, row, 2, 1 );
		row++;
		txtPurpose = new TextField();
		txtPurpose.setMinHeight( TextField.USE_PREF_SIZE );
		txtPurpose.setOnKeyReleased( new PurposeEventHandler() );
		add( txtPurpose, 1, row, 2, 1 );
		row++;

		// call sequence
		add( new Label( "Call Sequence" ), 1, row, 2, 1 );
		row++;
		callList = new ListView< MindFXCall >();
		add( callList, 1, row, 2, 1 );
		row++;
		cbCallAttribs = new ComboBox< String >();
		cbCallAttribs.setOnAction( new CallAttribsEventHandler() );
		add( cbCallAttribs, 1, row, 1, 1 );
		cbCallMethods = new ComboBox< String >();
		add( cbCallMethods, 2, row, 1, 1 );
		row++;
		b = new Button( "Add Call" );
		b.setOnAction( new AddCallEventHandler() );
		add( b, 1, row, 1, 1 );
		b = new Button( "Delete Selected Call" );
		b.setOnAction( new DeleteCallEventHandler() );
		add( b, 2, row, 1, 1 );
		row++;

		// simulation
		b = new Button( "Simulate" );
		b.setOnAction( new SimulationEventHandler() );
		add( b, 1, row, 2, 1 );
		row++;
	}

	public void setMethod( MindFXMethod m ) {
		fxmethod = m;
		label.setText( m.getFullCaption() );
		cbTypes.setValue( m.getType() );
		cbScope.setValue( m.getScope() );
		cbPointers.setValue( m.getPointers() );
		txtPurpose.setText( m.getPurpose() );
		paramList.setItems( m.getFXParams() );
		callList.setItems( m.getFXCalls() );
		cbCallAttribs.setItems( getAttributes() );
		cbCallAttribs.setValue( "this" );
		cbCallMethods.setItems( getMethods( "this" ) );
	}

	/**
	 * 
	 * @param type
	 * @return
	 */
	private ObservableList< String > getMethods( String type ) {
		if( type == null ) return null;
		ObservableList< String > list = FXCollections
				.< String >observableArrayList();
		// get this class
		MindFXClass c = fxmethod.getFXClass();

		if( type.equals( "this" ) ) {
			for( MindFXMethod m: c.getMethods() )
				list.add( m.getName() );
		} else {
			// get selected attribute
			MindFXAttribute a = c.getAttribute( type );
			MindFXClass c2;
			// get selected attributes class
			if( a != null )
				c2 = profiler.getFXClass( a.getType() );
			else {
				// get selected parameters class
				MindFXParam p = fxmethod.getFXParam( type );
				c2 = profiler.getFXClass( p.getType() );
			}
			// get public methods of the class
			for( MindFXMethod m: c2.getMethods() ) {
				if( m.getScope().equals( "public" ) ) list.add( m.getName() );
			}
		}

		return list;
	}

	private ObservableList< String > getAttributes() {
		MindFXClass c = fxmethod.getFXClass();
		ObservableList< String > list = FXCollections
				.< String >observableArrayList();
		list.add( "this" );
		// add attributes that belong the class of this method
		for( MindFXAttribute a: c.getAttributes() ) {
			// get only "object" types
			// not basic once like int, char etc.
			String type = a.getType();
			MindFXClass cl = profiler.getFXClass( type );
			if( cl != null ) list.add( a.getName() );
		}
		// get parameters
		for( MindFXParam p: fxmethod.getFXParams() ) {
			String type = p.getType();
			MindFXClass cl = profiler.getFXClass( type );
			if( cl != null ) list.add( p.getName() );
		}

		return list;
	}

}
